/**
 * @description 虚拟列表
 * @date 2024.9.10
 * @author wz
 * @midify 2025.5.15
 * @editor wz
 */
import { _decorator, CCBoolean, CCFloat, CCInteger, Component, Enum, EPSILON, EventTouch, instantiate, math, Node, NodePool, tween, Tween, UITransform, Vec3 } from 'cc';
import { M_Direction, V_Direction, H_Direction, V_Corner, H_Corner } from '../common/Enum';
import { ObjectPool } from '../utils/ObjectPool';
import { UIOpacity } from 'cc';
const { ccclass, property, disallowMultiple } = _decorator;

/**
 * 虚拟列表(单独写更清爽)
 */
@ccclass('CVirtualList')
@disallowMultiple()
export class CVirtualList extends Component {
    @property({ type: Node, tooltip: '滑动节点anchor(0.5,0.5)', group: 'CVirtualList' })
    view: Node;
    @property({ type: [Node], tooltip: 'item节点anchor(0.5,0.5)', group: 'CVirtualList' })
    items: Node[] = [];
    @property({ type: CCBoolean, displayName: '滚动惯性', tooltip: '滚动惯性', group: 'CVirtualList' })
    inertia: boolean = true;
    @property({ type: Enum(M_Direction), displayName: '主方向', tooltip: '竖屏 ,横屏', group: 'CVirtualList' })
    m_direction: M_Direction = M_Direction.VERTICAL;
    @property({ type: Enum(V_Direction), displayName: '垂直方向', tooltip: '从上到下或从下到上', visible() { return this.m_direction == M_Direction.VERTICAL }, group: 'CVirtualList' })
    v_direction: V_Direction = V_Direction.TOP_TO_BOTTOM;
    @property({ type: Enum(H_Direction), displayName: '水平方向', tooltip: '从左到右或从右到左', visible() { return this.m_direction == M_Direction.HORIZONTAL }, group: 'CVirtualList' })
    h_direction: H_Direction = H_Direction.LEFT_TO_RIGHT;
    @property({ type: Enum(V_Corner), displayName: '垂直方向StartCorner', tooltip: '从左往右或从右往左', visible() { return this.m_direction == M_Direction.VERTICAL }, group: 'CVirtualList' })
    v_corner: V_Corner = V_Corner.START_LEFT_RIGHT;
    @property({ type: Enum(H_Corner), displayName: '水平方向StartCorner', tooltip: '从左往右或从右往左', visible() { return this.m_direction == M_Direction.HORIZONTAL }, group: 'CVirtualList' })
    h_corner: H_Corner = H_Corner.START_TOP_BOTTOM;

    @property({ type: CCInteger, displayName: '纵向布局时的列数', tooltip: '纵向布局时的列数', visible() { return this.m_direction == M_Direction.VERTICAL }, group: 'CVirtualList' })
    colNum: number = 1;
    @property({ type: CCInteger, displayName: '横向布局时的行数', tooltip: '横向布局时的行数', visible() { return this.m_direction == M_Direction.HORIZONTAL }, group: 'CVirtualList' })
    rowNum: number = 1;

    @property({ type: CCFloat, displayName: 'paddingLeft', tooltip: '左边界和节点内边距', group: 'CVirtualList' })
    paddingLeft: number = 0;
    @property({ type: CCFloat, displayName: 'paddingRight', tooltip: '右边界和节点内边距', group: 'CVirtualList' })
    paddingRight: number = 0;
    @property({ type: CCFloat, displayName: 'paddingTop', tooltip: '上边界和节点内边距', group: 'CVirtualList' })
    paddingTop: number = 0;
    @property({ type: CCFloat, displayName: 'paddingBottom', tooltip: '下边界和节点内边距', group: 'CVirtualList' })
    paddingBottom: number = 0;
    @property({ type: CCFloat, displayName: 'spacingY', tooltip: '纵向间隔', group: 'CVirtualList' })
    spacingY: number = 0;
    @property({ type: CCFloat, displayName: 'spacingX', tooltip: '横向间隔', group: 'CVirtualList' })
    spacingX: number = 0
    @property({ type: CCBoolean, tooltip: '居中吸附', group: 'CVirtualList' })
    isCenter: boolean = false
    @property({ type: CCFloat, tooltip: '放大系数', visible() { return this.isCenter }, group: 'CVirtualList' })
    scale: number = 1.2
    @property({ type: CCFloat, tooltip: '最大放大距离', visible() { return this.isCenter }, group: 'CVirtualList' })
    scaleDis: number = 20

    private _initialize: boolean = false;
    protected _dataList: any[] = [];
    private _isCanTouch: boolean = false;
    private _touching: boolean = false;
    private _scrollSpeed: number = 0;
    private _touchStartTimestamp: number = 0;
    private _touchStartScrollOffset: number = 0;
    private _timestamp: number = 0;
    private _autoScroll: boolean = false;
    private _autoScrollTime: number = 0;
    private _maxAutoScollTIme: number = 2;
    private _scrollOffset: number = 0
    private _maxScroll: number = 0;
    private _isOverView: boolean = false;
    private _itemList: { itemIndex: number, index: number, node: Node, isEvent: boolean }[] = [];
    private _cachePosList: { x: number, y: number }[] = []
    private _poolMap: Map<number, ObjectPool<Node>> = new Map()
    private _scrollTrans: UITransform = null;
    private _centerValue: number = 0;
    /**
     * 行或者列
     */
    get centerValue() {
        return this._centerValue;
    }

    /**
     * 刷新
     */
    itemRender: (index: number, item: Node, data?: any) => void = null;
    /**
     * 提供对应的items的index
     */
    itemProvider: (index: number) => number = null;

    protected onLoad(): void {
        this._init()
    }

    protected onDestroy(): void {
        for (let [k, v] of this._poolMap.entries()) {
            v.clear()
        }
        this._poolMap.clear()
    }

    /**
     * 初始化设置
     */
    private _init() {
        if (this._initialize) return;
        this._initialize = true;
        // 注册事件
        this.view.on(Node.EventType.TOUCH_START, this.onTouchStart.bind(this), this, true);
        this.view.on(Node.EventType.TOUCH_MOVE, this.onTouchMove.bind(this), this, true);
        this.view.on(Node.EventType.TOUCH_END, this.onTouchEnd.bind(this), this, true);
        this.view.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd.bind(this), this, true);
        // 初始化items
        this.items.forEach(item => {
            item.active = false;
            item.getComponent(UITransform).setAnchorPoint(0.5, 0.5)
        })
        // 初始化scroll
        this._scrollTrans = this.view.getComponent(UITransform);
        this._scrollTrans.setAnchorPoint(0.5, 0.5);
        const itemTrans = this.items[0].getComponent(UITransform)
        if (this.m_direction == M_Direction.VERTICAL)
            this._scrollTrans.width = this.paddingLeft + this.paddingRight + this.colNum * itemTrans.width + this.spacingX * (this.colNum - 1)
        else if (this.m_direction == M_Direction.HORIZONTAL)
            this._scrollTrans.height = this.paddingBottom + this.paddingTop + this.rowNum * itemTrans.height + this.spacingY * (this.rowNum - 1)

    }

    /**
     * 重置信息
     */
    private _resetInfo() {
        this._touching = false;
        this._autoScroll = false;
        this._autoScrollTime = 0;
        Tween.stopAllByTarget(this.node);
    }

    /**
     * 提前计算一些必要信息
     */
    private _calcInfo() {
        // 重置缓存信息
        this._cachePosList.length = 0;

        // 设置初始位置
        let lastDis = 0;
        if (this.m_direction == M_Direction.VERTICAL)
            lastDis = this.v_direction == V_Direction.TOP_TO_BOTTOM ? this._scrollTrans.height / 2 : -this._scrollTrans.height / 2
        else
            lastDis = this.h_direction == H_Direction.LEFT_TO_RIGHT ? -this._scrollTrans.height / 2 : this._scrollTrans.height / 2
        // 设置默认数据
        this._maxScroll = 0
        let row = 0, col = 0, posX = 0, posY = 0;
        for (let i = 0; i < this._dataList.length; i++) {
            let { itemWidth, itemHeight } = this._getItemWH(i);
            if (this.m_direction == M_Direction.VERTICAL) {
                row = Math.floor(i / this.colNum)
                col = i % this.colNum;
                if (this.v_corner == V_Corner.START_LEFT_RIGHT)
                    posX = -this._scrollTrans.width / 2 + this.paddingLeft + (col + 0.5) * itemWidth + col * this.spacingX
                else
                    posX = this._scrollTrans.width / 2 - this.paddingRight - (col + 0.5) * itemWidth - col * this.spacingX;
                if (this.v_direction == V_Direction.TOP_TO_BOTTOM)
                    posY = lastDis - itemHeight - (row == 0 ? this.paddingTop : this.spacingY)
                else
                    posY = lastDis + itemHeight + (row == 0 ? this.paddingBottom : this.spacingY)
                // 在下一行之前保存posY
                if (col == this.colNum - 1) lastDis = posY
                // 这是最终计算位置
                posY += this.v_direction == V_Direction.TOP_TO_BOTTOM ? itemHeight / 2 : -itemHeight / 2
                // 在第一列时计算长度
                if (col == 0) {
                    this._maxScroll += row == 0 ? this.paddingTop + this.paddingBottom : this.spacingY
                    this._maxScroll += itemHeight
                }
            } else if (this.m_direction == M_Direction.HORIZONTAL) {
                row = i % this.rowNum
                col = Math.floor(i / this.rowNum)
                if (this.h_corner == H_Corner.START_TOP_BOTTOM)
                    posY = this._scrollTrans.height / 2 - this.paddingTop - (row + 0.5) * itemHeight - row * this.spacingY
                else
                    posY = -this._scrollTrans.height / 2 + this.paddingBottom + (row + 0.5) * itemHeight + row * this.spacingY;
                if (this.h_direction == H_Direction.LEFT_TO_RIGHT)
                    posX = lastDis + itemWidth + (col == 0 ? this.paddingLeft : this.spacingX)
                else
                    posX = lastDis - itemWidth - (col == 0 ? this.paddingRight : this.spacingX)
                // 在下一列之前保存posX
                if (row == this.rowNum - 1) lastDis = posX
                // 这是最终计算位置
                posX += this.h_direction == H_Direction.LEFT_TO_RIGHT ? -itemWidth / 2 : itemWidth / 2
                // 在第一行时计算长度
                if (row == 0) {
                    this._maxScroll += col == 0 ? this.paddingLeft + this.paddingRight : this.spacingX
                    this._maxScroll += itemWidth
                }
            }
            // 保存最后一个的位置
            this._cachePosList.push({ x: posX, y: posY })
        }
        this._maxScroll -= this.m_direction == M_Direction.VERTICAL ? this._scrollTrans.height : this._scrollTrans.width
        this._isOverView = this._maxScroll > 0
    }



    private _elastic(ratio: number) {
        return ratio * ratio;
    }

    private _deltaElastic(delta: number) {
        // 显示未超出不让移动
        if (!this._isOverView) return 0
        let dis = 0
        if ((this.m_direction == M_Direction.VERTICAL && this.v_direction == V_Direction.TOP_TO_BOTTOM) ||
            (this.m_direction == M_Direction.HORIZONTAL && this.h_direction == H_Direction.RIGHT_TO_LEFT)) {
            if (this._scrollOffset < 0 && delta < 0)
                dis = this._scrollOffset
            else if (this._scrollOffset > this._maxScroll && delta > 0)
                dis = this._scrollOffset - this._maxScroll
        }
        if ((this.m_direction == M_Direction.VERTICAL && this.v_direction == V_Direction.BOTTOM_TO_TOP) ||
            (this.m_direction == M_Direction.HORIZONTAL && this.h_direction == H_Direction.LEFT_TO_RIGHT)) {
            if (this._scrollOffset > 0 && delta > 0)
                dis = this._scrollOffset
            else if (-this._scrollOffset > this._maxScroll && delta < 0)
                dis = -this._scrollOffset - this._maxScroll
        }
        if (dis != 0)
            delta *= 1 - this._elastic(math.clamp01(Math.abs(dis) / 100.0));
        return delta;
    }

    /**
     * 滑动是否超出边界
     * @returns 
     */
    private _isOutOfBorder() {
        if ((this.m_direction == M_Direction.VERTICAL && this.v_direction == V_Direction.TOP_TO_BOTTOM) ||
            (this.m_direction == M_Direction.HORIZONTAL && this.h_direction == H_Direction.RIGHT_TO_LEFT)) {
            if (this._scrollOffset < 0 || this._scrollOffset > this._maxScroll)
                return true
        }
        if ((this.m_direction == M_Direction.VERTICAL && this.v_direction == V_Direction.BOTTOM_TO_TOP) ||
            (this.m_direction == M_Direction.HORIZONTAL && this.h_direction == H_Direction.LEFT_TO_RIGHT)) {
            if (this._scrollOffset > 0 || -this._scrollOffset > this._maxScroll)
                return true
        }
        return false
    }

    /**
     * 移动到边界的偏移
     * @returns 
     */
    private _getOutToBorderOffset() {
        // 回到原始位置
        if (!this._isOverView) return 0
        let borderOffset = 0
        if ((this.m_direction == M_Direction.VERTICAL && this.v_direction == V_Direction.TOP_TO_BOTTOM) ||
            (this.m_direction == M_Direction.HORIZONTAL && this.h_direction == H_Direction.RIGHT_TO_LEFT)) {
            if (this._scrollOffset < 0)
                borderOffset = 0
            else if (this._scrollOffset > this._maxScroll)
                borderOffset = this._maxScroll
        }
        if ((this.m_direction == M_Direction.VERTICAL && this.v_direction == V_Direction.BOTTOM_TO_TOP) ||
            (this.m_direction == M_Direction.HORIZONTAL && this.h_direction == H_Direction.LEFT_TO_RIGHT)) {
            if (this._scrollOffset > 0)
                borderOffset = 0
            else if (-this._scrollOffset > this._maxScroll)
                borderOffset = -this._maxScroll
        }
        return borderOffset;
    }



    /**
     * 计算移动到指定index的偏移量方向和_scrollOffset一致
     * @param index 
     * @param bordFirst 不同方向的第一个边
     * @returns 
     */
    private _getMoveToIndexOffset(index: number, isReverse = false) {
        if (!this._isOverView) return 0
        let offset = 0
        const { x, y } = this._cachePosList[index]
        const { itemWidth, itemHeight } = this._getItemWH(index)
        if (this.m_direction == M_Direction.VERTICAL) {
            let posy = 0, row = Math.floor(index / this.colNum)
            if ((this.v_direction == V_Direction.TOP_TO_BOTTOM && !isReverse) ||
                (this.v_direction == V_Direction.BOTTOM_TO_TOP && isReverse))
                posy = this._scrollTrans.height / 2 - itemHeight / 2 - (row == 0 ? this.paddingTop : this.spacingY)
            else
                posy = -this._scrollTrans.height / 2 + itemHeight / 2 + (row == 0 ? this.paddingBottom : this.spacingY)
            offset = Math.abs(y - posy)
            offset = Math.min(offset, this._maxScroll)
            offset = this.v_direction == V_Direction.TOP_TO_BOTTOM ? offset : -offset
        }
        else if (this.m_direction == M_Direction.HORIZONTAL) {
            let posx = 0, col = Math.floor(index / this.rowNum)
            if ((this.h_direction == H_Direction.LEFT_TO_RIGHT && !isReverse) ||
                (this.h_direction == H_Direction.RIGHT_TO_LEFT && isReverse))
                posx = -this._scrollTrans.width / 2 + itemWidth / 2 + (col == 0 ? this.paddingLeft : this.spacingX)
            else
                posx = this._scrollTrans.width / 2 - itemWidth / 2 - (col == 0 ? this.paddingRight : this.spacingX)
            offset = Math.abs(x - posx)
            offset = Math.min(offset, this._maxScroll)
            offset = this.h_direction == H_Direction.LEFT_TO_RIGHT ? -offset : offset
        }
        return offset
    }

    /**
     * 把指定index移动到目标位置的偏移量
     * @param index 
     * @param targetPos 
     * @returns 
     */
    private _getMovePosOffset(index: number, targetPos: { x: number, y: number }) {
        let offset = 0
        const { x, y } = this._cachePosList[index]
        if (this.m_direction == M_Direction.VERTICAL)
            offset = targetPos.y - y
        else if (this.m_direction == M_Direction.HORIZONTAL)
            offset = targetPos.x - x
        return offset
    }

    /**
     * 移动到指定的offset内
     * @param targetOffset 
     * @param callback 
     * @returns 
     */
    private _moveToOffset(targetOffset: number, moveTime: number, callback?) {
        if (targetOffset == this._scrollOffset) {
            callback && callback()
            return
        }
        Tween.stopAllByTarget(this.node);
        if (moveTime <= 0) {
            this._scrollOffset = targetOffset;
            this._updateItem()
            callback && callback()
            return
        }
        let scrollOffset = this._scrollOffset;
        tween(this.node)
            .to(moveTime, {}, {
                onUpdate: (target, ratio: number) => {
                    this._scrollOffset = math.lerp(scrollOffset, targetOffset, ratio)
                    this._updateItem();
                }
            })
            .call(() => callback && callback())
            .start()
    }

    /**
     * 移动到边界
     */
    private _moveToBorder(time = 0.2) {
        let offset = this._getOutToBorderOffset();
        this._moveToOffset(offset, time);
    }

    /**
     * 移动到中间
     * @returns 
     */
    private _moveToCenter(time = 0.2) {
        if (!this.isCenter) return
        let dis = Number.MAX_VALUE;
        for (let i = 0; i < this._itemList.length; i++) {
            let itemDis = 0, itemInfo = this._itemList[i]
            itemDis = this.m_direction == M_Direction.VERTICAL ? itemInfo.node.position.y : itemInfo.node.position.x
            if (Math.abs(dis) > Math.abs(itemDis)) {
                dis = itemDis
                this._centerValue = itemInfo.index
            }
        }
        // 转换成行或者列
        if (this.m_direction == M_Direction.VERTICAL)
            this._centerValue = Math.floor(this._centerValue / this.colNum)
        else
            this._centerValue = Math.floor(this._centerValue % this.rowNum)
        console.log('中间值-----：' + this._centerValue)
        this._moveToOffset(this._scrollOffset - dis, time)
    }


    protected onTouchStart(event: EventTouch) {
        if (!this._isCanTouch) return
        this._touching = true;
        this._autoScroll = false;
        this._touchStartTimestamp = this._timestamp = new Date().getTime() / 1000;
        this._touchStartScrollOffset = this._scrollOffset;
        Tween.stopAllByTarget(this.node);
    }

    protected onTouchMove(event: EventTouch) {
        if (!this._isCanTouch) return
        if (this._touching) {
            // 手指 向上和向右 delta > 0
            let delta = this.m_direction == M_Direction.VERTICAL ? event.getDelta().y : event.getDelta().x
            delta = this._deltaElastic(delta);
            // 设置滑动距离
            this._scrollOffset += delta;

            let timestamp = new Date().getTime() / 1000;
            this._scrollSpeed = delta / Math.max(0.001, timestamp - this._timestamp);
            this._timestamp = timestamp;

            console.log('0------_scrollOffset:' + this._scrollOffset + '-------delta:' + delta);
            try {
                this._updateItem();
            } catch (error) {
                console.error('virtual onTouchMove error:' + error)
            }

        }
    }

    /**
     * 是否是按钮点击
     * @returns 
     */
    private _isTouchClick() {
        if (this._touchStartTimestamp - this._timestamp < 0.5 && Math.abs(this._scrollOffset - this._touchStartScrollOffset) < 2) {
            console.log('is click')
            return true
        }
        return false
    }

    protected onTouchEnd(event: EventTouch) {
        console.log('****virtual touch end')
        if (!this._isCanTouch) return
        this._touching = false;
        this._timestamp = new Date().getTime() / 1000;

        // 在中心吸附模式下
        if (this.isCenter) {
            // 点击跳过
            if (this._isTouchClick()) return
            event.propagationStopped = true
            this._moveToCenter()
        } else {
            // 列表状态滑动出界回复运动
            if (this._isOutOfBorder()) {
                event.propagationStopped = true
                console.log('****move to border')
                this._moveToBorder();
                return;
            }
            // 点击跳过
            if (this._isTouchClick()) return
            event.propagationStopped = true
            this._autoScroll = true;
            this._autoScrollTime = this._maxAutoScollTIme;
        }
    }

    protected update(deltaTime: number) {
        if (this._autoScroll && this.inertia) {
            this._autoScrollTime -= deltaTime
            if (this._autoScrollTime < 0) {
                this._autoScroll = false;
                this._autoScrollTime = 0;
            }

            let factor = this._autoScrollTime / this._maxAutoScollTIme
            this._scrollSpeed *= this._elastic(factor);
            let delta = this._scrollSpeed * deltaTime;
            delta = this._deltaElastic(delta)

            this._scrollOffset += delta
            console.log('factor:' + factor + '------_scrollSpeed:' + this._scrollSpeed)
            console.log('1------_scrollOffset:' + this._scrollOffset + '-------delta:' + delta);
            this._updateItem();

            //列表状态,惯性滑动出界且慢慢停止时回复到边界运动
            if (Math.abs(delta) < EPSILON) {
                this._autoScroll = false;
                this._autoScrollTime = 0;
                if (this._isOutOfBorder())
                    this._moveToBorder()
            }
        }
    }

    /**
     * 获取items内的index
     * @param index 
     * @returns 
     */
    private _getItemIndex(index: number) {
        let itemIndex = 0;
        // 暂时不支持多行多列自定义node大小
        if ((this.m_direction == M_Direction.VERTICAL && this.colNum > 1) ||
            (this.m_direction == M_Direction.HORIZONTAL && this.rowNum > 1))
            itemIndex = 0
        else if (this.itemProvider)
            itemIndex = this.itemProvider(index)
        return itemIndex;
    }

    /**
     * 获取节点的宽高
     * @param index 
     * @returns 
     */
    private _getItemWH(index: number) {
        const itemIndex = this._getItemIndex(index)
        const itemTrans = this.items[itemIndex]?.getComponent(UITransform)
        return { itemWidth: itemTrans.width, itemHeight: itemTrans.height }
    }

    /**
     * 检查池子在不在
     * @param itemIndex 
     */
    private _checkPool(itemIndex: number) {
        /**
         * 解决一个罕见的bug
         * 当滑动框内节点调用node.on()事件后, 当在滑动框内首次点击到该节点上，在滑动过程中
         * 如果该节点变成false或者从父结点移除等，或造成滑动框的内监听的touchEnd事件无响应
         * 主要原因当事件收集阶段时发现该节点失效而导致事件冒泡无效
         * 现在暂时改成透明度调整
         */
        if (!this._poolMap.has(itemIndex)) {
            this._poolMap.set(itemIndex, new ObjectPool({
                onCreate: () => {
                    const itemNode = instantiate(this.items[itemIndex])
                    itemNode.parent = this.view
                    itemNode.active = true
                    const opacity = itemNode.getComponent(UIOpacity) || itemNode.addComponent(UIOpacity);
                    opacity.opacity = 255
                    return itemNode
                }, // 创建节点
                onUse: (obj) => obj.getComponent(UIOpacity).opacity = 255, // 使用节点时的回调
                onRecycle: (obj) => obj.getComponent(UIOpacity).opacity = 0, // 回收节点时的回调
            }))
        }
    }

    /**
     * 获取节点
     * @param itemIndex 
     */
    private _getItemNode(itemIndex: number) {
        this._checkPool(itemIndex)
        return this._poolMap.get(itemIndex).get()
    }

    /**
     * 回收节点
     * @param itemIndex 
     * @param node 
     */
    private _putItemNode(itemIndex: number, node: Node) {
        this._checkPool(itemIndex)
        this._poolMap.get(itemIndex).put(node)
    }

    /**
     * 设置节点
     * @param indexInfoArr 
     * @returns 
     */
    private _setItems(indexInfoArr: { index: number, posX: number, posY: number }[]) {
        const showList = [];
        for (let i = 0; i < indexInfoArr.length; i++) {
            const { index, posX, posY } = indexInfoArr[i]
            const itemIndex = this._getItemIndex(index)
            const fIndex = this._itemList.findIndex(a => a.index == index && itemIndex == itemIndex)
            let itemInfo: { itemIndex: number, index: number, node: Node, isEvent: boolean };
            if (fIndex >= 0) {
                itemInfo = this._itemList[fIndex]
                this._itemList.splice(fIndex, 1)
            } else {
                const itemNode = this._getItemNode(itemIndex)
                itemInfo = { itemIndex: itemIndex, index: index, node: itemNode, isEvent: true }
            }
            showList.push(itemInfo)
            itemInfo.node.setPosition(posX, posY)
            // 设置缩放
            this._setItemCenterScale(itemInfo.node, itemIndex)
            if (this.itemRender && itemInfo.isEvent) {
                itemInfo.isEvent = false
                this.itemRender(index, itemInfo.node, this._dataList[index])
            }
        }
        // 回收
        for (let i = 0; i < this._itemList.length; i++) {
            const itemInfo = this._itemList[i]
            this._putItemNode(itemInfo.itemIndex, itemInfo.node)
        }
        this._itemList = showList;
    }

    /**
     * 设置缩放
     */
    private _setItemCenterScale(node: Node, itemIndex: number) {
        // 设置缩放
        if (this.isCenter) {
            let dis = this.m_direction == M_Direction.HORIZONTAL ? Math.abs(node.position.x) : Math.abs(node.position.y)
            if (dis < this.scaleDis)
                node.setScale(this.items[itemIndex].scale.clone().multiplyScalar(1 + (this.scaleDis - dis) / this.scaleDis))
            else
                node.setScale(this.items[itemIndex].scale.clone())
        }
    }

    /**
     * 更新节点
     */
    private _updateItem() {
        let indexInfoArr: { index: number, posX: number, posY: number }[] = []
        for (let i = 0; i < this._dataList.length; i++) {
            const { itemWidth, itemHeight } = this._getItemWH(i);
            const { x, y } = this._cachePosList[i]
            if (this.m_direction == M_Direction.VERTICAL) {
                // 在scroll内的真实位置
                const realPosY = this._scrollOffset + y;
                // 在scroll区域内要显示的item
                if (!(realPosY - itemHeight / 2 > this._scrollTrans.height / 2 || realPosY + itemHeight / 2 < -this._scrollTrans.height / 2)) {
                    indexInfoArr.push({ index: i, posX: x, posY: realPosY })
                }
            } else {
                // 在scroll内的真实位置
                const realPosX = this._scrollOffset + x;
                // 在scroll区域内要显示的item
                if (!(realPosX - itemWidth / 2 > this._scrollTrans.width / 2 || realPosX + itemWidth / 2 < -this._scrollTrans.width / 2)) {
                    indexInfoArr.push({ index: i, posX: realPosX, posY: y })
                }
            }
        }
        this._setItems(indexInfoArr)
    }

    /**
     * 刷新数据显示
     * @param dataList 显示的数据
     * @param delayTime 延迟显示item时间 
     */
    updateData(dataList: any[]) {
        this._init();
        this._isCanTouch = true;
        this._dataList = dataList;
        // 重置数据
        this._resetInfo()
        // 重新计算
        this._calcInfo();
        // 所有都刷新一次
        this._itemList.forEach(a => a.isEvent = true)
        this._updateItem();

        if (this.isCenter) {
            this._moveToCenter(0)
            return
        }

        if (this._isOverView) {
            this._scrollOffset = 0;
            this._updateItem();
            return
        }

        if (this._isOutOfBorder())
            this._moveToBorder()
    }


    /**
     * 移动到指定index(居中吸附 模式下不可用)
     * @param index 
     * @param time 
     */
    scrollToIndex(index: number, time = 0, callback?) {
        if (!this._isCanTouch || this.isCenter) return
        this._resetInfo()
        const offset = this._getMoveToIndexOffset(index)
        this._moveToOffset(offset, time, callback)
    }

    /**
     * 移到显示框内(居中吸附 模式下不可用)
     * @param index 
     * @param time 
     * @param callback 
     * @returns 
     */
    scrollToView(index: number, time = 0, callback?) {
        if (!this._isCanTouch || this.isCenter) return
        this._resetInfo()
        const offset1 = this._getMoveToIndexOffset(index)
        const offset2 = this._getMoveToIndexOffset(index, true)
        const offset = Math.abs(offset1 - this._scrollOffset) < Math.abs(offset2 - this._scrollOffset) ? offset1 : offset2
        this._moveToOffset(offset, time, callback)
    }

    /**
     * 移动到指定index(非居中吸附 模式下不可用)
     * @param index 
     * @param time 
     */
    scrollToCenterIndex(index: number, time = 0, callback?) {
        if (!this._isCanTouch || !this.isCenter) return
        this._resetInfo()
        const offset = this._getMovePosOffset(index, { x: 0, y: 0 })
        this._moveToOffset(offset, time, callback)
    }

    /**
     * 移动到底(居中吸附 模式下不可用)
     * @param time 
     * @param callback 
     */
    scrollToBottom(time = 0, callback?) {
        if (!this._isCanTouch || this.isCenter) return
        this._resetInfo()
        let offset = 0
        if (this._isOverView && this.m_direction == M_Direction.VERTICAL && this.v_direction == V_Direction.TOP_TO_BOTTOM)
            offset = this._maxScroll
        this._moveToOffset(offset, time, callback)
    }

    /**
     * 移动到顶(居中吸附 模式下不可用)
     * @param time 
     * @param callback 
     */
    scrollToTop(time = 0, callback?) {
        if (!this._isCanTouch || this.isCenter) return
        this._resetInfo()
        let offset = 0
        if (this._isOverView && this.m_direction == M_Direction.VERTICAL && this.v_direction == V_Direction.BOTTOM_TO_TOP)
            offset = -this._maxScroll
        this._moveToOffset(offset, time, callback)
    }

    /**
     * 移动到右(居中吸附 模式下不可用)
     * @param time 
     * @param callback 
     */
    scrollToRight(time = 0, callback?) {
        if (!this._isCanTouch || this.isCenter) return
        this._resetInfo()
        let offset = 0
        if (this._isOverView && this.m_direction == M_Direction.HORIZONTAL && this.h_direction == H_Direction.LEFT_TO_RIGHT)
            offset = -this._maxScroll
        this._moveToOffset(offset, time, callback)
    }

    /**
     * 移动到左(居中吸附 模式下不可用)
     * @param time 
     * @param callback 
     */
    scrollToLeft(time = 0, callback?) {
        if (!this._isCanTouch || this.isCenter) return
        this._resetInfo()
        let offset = 0
        if (this._isOverView && this.m_direction == M_Direction.HORIZONTAL && this.h_direction == H_Direction.RIGHT_TO_LEFT)
            offset = this._maxScroll
        this._moveToOffset(offset, time, callback)
    }

    /**
     * 移除一个item(单行或者单列时且只使用一个item时使用才是正常效果)
     * @param index 
     */
    removeIndex(index: number, time = 0.2, callback?) {
        if (!this._isCanTouch) return;
        // 移除数据
        this._dataList.splice(index, 1)
        const fIndex = this._itemList.findIndex(a => a.index == index)
        if (fIndex < 0 || time <= 0) {
            this.updateData(this._dataList)
            callback && callback()
            return;
        }
        // 重置数据
        this._resetInfo()
        // 禁止触摸
        this._isCanTouch = false
        // 保存要移动信息
        const posList: { startPos: Vec3, endPos: Vec3 }[] = []
        const itemList: { itemIndex: number, index: number, node: Node, isEvent: boolean }[] = []
        for (let i = fIndex + 1; i < this._itemList.length; i++) {
            posList.push({ startPos: this._itemList[i].node.position.clone(), endPos: this._itemList[i - 1].node.position.clone() })
            itemList.push(this._itemList[i])
        }

        // 回收移除节点
        this._putItemNode(this._itemList[fIndex].index, this._itemList[fIndex].node)
        this._itemList.splice(fIndex, 1)

        tween(this.node)
            .to(time, {}, {
                onUpdate: (target, ratio: number) => {
                    for (let i = 0; i < itemList.length; i++) {
                        itemList[i].node.setPosition(Vec3.lerp(new Vec3, posList[i].startPos, posList[i].endPos, ratio))
                        this._setItemCenterScale(itemList[i].node, itemList[i].itemIndex)
                    }
                }
            })
            .call(() => {
                this.updateData(this._dataList)
                callback && callback()
            })
            .start()
    }
}


